package com.joymart.compute.service;


import com.joymart.common.model.Range;
import com.joymart.common.system.BusinessConfig;
import com.joymart.common.system.IdGenerator;
import com.joymart.common.utils.DatetimeUtils;
import com.joymart.compute.model.AmountAggregation;
import com.joymart.compute.model.OrderGain;
import com.joymart.compute.model.PvAggregation;
import com.joymart.compute.repository.OrderGainRepository;
import com.joymart.order.model.Order;
import com.joymart.order.model.OrderEvent;
import com.joymart.order.service.OrderEventService;
import com.joymart.user.model.BusinessRole;
import com.joymart.user.model.CachedUser;
import com.joymart.user.service.UserCacheManager;
import jdk.jfr.Event;
import jdk.jfr.EventType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.List;

@Service
@Transactional
public class OrderGainService {

    @Autowired
    private UserCacheManager userCacheManager;

    @Autowired
    private IdGenerator idGenerator;
    @Autowired
    private BusinessConfig businessConfig;

    @Autowired
    private JdbcAggregateTemplate jdbcAggregateTemplate;
    @Autowired
    private OrderGainRepository orderGainRepository;
    @Autowired
    private OrderEventService orderEventService;


    /**
     * 查询区间内的订单收益
     *
     * @param ownerId
     * @param type
     * @param range
     * @return
     */
    public PvAggregation aggregateOrderGain(String ownerId, OrderGain.Type type, Range range) {
        return orderGainRepository.aggregateOrderGain(ownerId, type, range.getStart(), range.getEnd());
    }
    /**
     * 查询区间内的育成利润
     *
     * @param ownerId
     * @param range
     * @return
     */
    public AmountAggregation aggregateSecondSellerProfit(String ownerId, Range range) {
        List<String> firstSellerPaidOrders = orderEventService.search("FIRST_SELLER_PAID", range, ownerId).stream().map(OrderEvent::getOrderId).toList();
        if(firstSellerPaidOrders.isEmpty()){
            return new AmountAggregation(0,BigDecimal.ZERO);
        }
        return orderGainRepository.aggregateSecondSellerProfit(firstSellerPaidOrders);
    }


    /**
     * 查询区间内的自营利润
     *
     * @param ownerId
     * @param range
     * @return
     */
    public AmountAggregation aggregateFirstSellerProfit(String ownerId, Range range) {
        List<String> buyerPaidOrders = orderEventService.search("BUYER_PAID", range, ownerId).stream().map(OrderEvent::getOrderId).toList();
        if(buyerPaidOrders.isEmpty()){
            return new AmountAggregation(0,BigDecimal.ZERO);
        }
        return orderGainRepository.aggregateFirstSellerProfit(buyerPaidOrders);
    }

    public void computeOrderGain(Order order) {
        computeWHSGain(order);
        computeAgentGain(order);
        computeManagerGain(order);
        computeAgDirectorGain(order);
        computeAuDirectorGain(order);
    }


    private void computeAgDirectorGain(Order order) {
        CachedUser agDirector;

        CachedUser firstSeller = userCacheManager.get(order.getFirstSellerId());
        if(firstSeller.getBusinessRoles().contains(BusinessRole.AG_DIRECTOR)) {
            agDirector = firstSeller;
        }else {
            agDirector = userCacheManager.upwardSearchUntil(firstSeller, u->u.getBusinessRoles().contains(BusinessRole.AG_DIRECTOR));
        }

        if(agDirector == null){
            return;
        }
        saveNewGainInstance(order, OrderGain.Type.AG_DIRECTOR_GAIN, agDirector.getId());

    }

    private void computeAuDirectorGain(Order order) {
        CachedUser auDirector;

        CachedUser secondSeller = userCacheManager.get(order.getSecondSellerId());
        if (secondSeller.getBusinessRoles().contains(BusinessRole.AU_DIRECTOR)) {
            auDirector = secondSeller;
        } else {
            auDirector = userCacheManager.upwardSearchUntil(secondSeller, u -> u.getBusinessRoles().contains(BusinessRole.AU_DIRECTOR));
        }

        if (auDirector == null) {
            return;
        }
        saveNewGainInstance(order, OrderGain.Type.AU_DIRECTOR_GAIN, auDirector.getId());
    }

    private void computeManagerGain(Order order) {
        CachedUser manager;

        CachedUser firstSeller = userCacheManager.get(order.getFirstSellerId());
        if(firstSeller.getBusinessRoles().contains(BusinessRole.MANAGER)) {
            manager = firstSeller;
        }else {
            manager = userCacheManager.upwardSearchUntil(firstSeller, u->u.getBusinessRoles().contains(BusinessRole.MANAGER));
        }

        if(manager == null){
            return;
        }
        saveNewGainInstance(order, OrderGain.Type.MANAGER_GAIN, manager.getId());
    }

    private void computeAgentGain(Order order) {
        CachedUser agAgent;

        CachedUser firstSeller = userCacheManager.get(order.getFirstSellerId());
        if(firstSeller.getBusinessRoles().contains(BusinessRole.AG_AGENT)) {
            agAgent = firstSeller;
        }else {
            agAgent = userCacheManager.upwardSearchUntil(firstSeller, u->u.getBusinessRoles().contains(BusinessRole.AG_AGENT));
        }

        if(agAgent == null){
            return;
        }
        saveNewGainInstance(order, OrderGain.Type.AG_AGENT_GAIN, agAgent.getId());

        CachedUser auAgent = userCacheManager.upwardSearchUntil(agAgent, u->u.getBusinessRoles().contains(BusinessRole.AU_AGENT));
        if(auAgent == null){
            return;
        }
        saveNewGainInstance(order, OrderGain.Type.AU_AGENT_GAIN, auAgent.getId(), agAgent.getId());

    }

    private void computeWHSGain(Order order) {
        CachedUser agWhs;

        CachedUser firstSeller = userCacheManager.get(order.getFirstSellerId());
        if(firstSeller.getBusinessRoles().contains(BusinessRole.AG_WHS)) {
            agWhs = firstSeller;
        }else {
           agWhs = userCacheManager.upwardSearchUntil(firstSeller, u->u.getBusinessRoles().contains(BusinessRole.AG_WHS));
        }

        if(agWhs == null){
            return;
        }
        saveNewGainInstance(order, OrderGain.Type.AG_WHS_GAIN, agWhs.getId());

        CachedUser auWhs = userCacheManager.upwardSearchUntil(agWhs, u->u.getBusinessRoles().contains(BusinessRole.AU_WHS));
        if(auWhs == null){
            return;
        }
        saveNewGainInstance(order, OrderGain.Type.AU_WHS_GAIN, auWhs.getId(), agWhs.getId());

    }

    private void saveNewGainInstance(Order order, OrderGain.Type type, String ownerId){
        this.saveNewGainInstance(order, type, ownerId, order.getFirstSellerId());
    }

    private void saveNewGainInstance(Order order, OrderGain.Type type, String ownerId, String contributorId){
        OrderGain orderGain = new OrderGain();
        orderGain.setId(idGenerator.next());
        orderGain.setOwnerId(ownerId);
        orderGain.setOrderId(order.getId());
        orderGain.setType(type);
        orderGain.setOrderOriginPv(order.getTotalPv());
        orderGain.setContributorId(contributorId);
        orderGain.setCreatedAt(DatetimeUtils.now());
        BigDecimal amount;
        switch (type) {
            case AG_WHS_GAIN:
                amount = order.getTotalPv().multiply(businessConfig.getAgWhsGainRate(), MathContext.DECIMAL64);
                break;
            case AU_WHS_GAIN:
                amount = order.getTotalPv().multiply(businessConfig.getAuWhsGainRate(), MathContext.DECIMAL64);
                break;
            case AG_AGENT_GAIN:
                amount = order.getTotalPv().multiply(businessConfig.getAgAgentGainRate(), MathContext.DECIMAL64);
                break;
            case AU_AGENT_GAIN:
                amount = order.getTotalPv().multiply(businessConfig.getAuAgentGainRate(), MathContext.DECIMAL64);
                break;
            case MANAGER_GAIN:
                amount = order.getTotalPv().multiply(businessConfig.getManagerGainRate(), MathContext.DECIMAL64);
                break;
            case AG_DIRECTOR_GAIN:
                amount = order.getTotalPv().multiply(businessConfig.getAgDirectorGainRate(), MathContext.DECIMAL64);
                break;
            case AU_DIRECTOR_GAIN:
                amount = order.getTotalPv().multiply(businessConfig.getAuDirectorGainRate(), MathContext.DECIMAL64);
                break;
            default:
                amount = BigDecimal.ZERO;
                break;
        }
        orderGain.setAmount(amount);
        jdbcAggregateTemplate.insert(orderGain);
    }

    public List<OrderGain> findByOrderId(String orderId) {
        return  orderGainRepository.findAllByOrderId(orderId);
    }
}
